package com.dashuai.modbus.core.master;

import com.dashuai.modbus.param.BatchReadParam;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.*;

import java.util.List;
import java.util.Map;

/**
 * <p>
 *
 * </p>
 *
 * @author QM
 * @date 2023/7/28
 */
public interface ModbusBaseMaster {

    ModbusFactory modbusFactory = new ModbusFactory();


    /**
     * 校验从设备id和地址位的合法性
     * @param slaveId 从设备好
     * @param address 地址位
     */
    default void checkSlaveIdAndAddress(int slaveId, int address) throws ModbusTransportException {

        if (slaveId < 1 || slaveId > 247) {
            throw new IllegalSlaveIdException("The slaveId is out of range");
        }

        if (address < 0 || address > 9999) {
            throw new IllegalDataAddressException(slaveId);
        }
    }


    /**
     * 校验数据类型合法性
     * @param dataType modbus 数据类型，参考 DataType 类
     * @param returnType 返回的数据类型
     * @param <T> 返回的数据类型
     */
    default <T> void checkDataType(int dataType, Class<T> returnType) {

        Class<?> expectedClass = DataType.getJavaType(dataType);
        if (expectedClass == null) {
            throw new IllegalDataTypeException("Unsupported dataType: " + dataType);
        }
        if (!expectedClass.equals(returnType)) {
            throw new IllegalDataTypeException("Mismatch between dataType and class: expected " + expectedClass.getName() + ", but got " + returnType.getName());
        }
    }


    /**
     * 读取[01 Coil Status 0x]类型。开关数据，01表示功能码，0x表示地址首位为0开头
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return 开关量
     */
    Boolean readCoilStatus(int slaveId, int offset)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException;

    /**
     * 读取[02 Input Status 1x]类型。开关数据，02表示功能码，1x表示地址首位为1开头
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return 开关量
     */
    Boolean readInputStatus(int slaveId, int offset)
            throws ModbusTransportException, ErrorResponseException;

    // TODO 考虑根据 modbus 的 dataType 和 java 的包装类型建立一个映射表，或者新增一个泛型参数，你传进来什么类型，我就返回什么类型
    /**
     * 读取[03 Holding Register 4x]类型。模拟量数据，03表示功能码，4x表示地址首位为4开头
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return 模拟量 = 整数、小数
     */
    Number readHoldingRegister(int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException;

    /**
     * 自定义类型
     * @param slaveId 从设备id
     * @param offset 地址位
     * @param dataType 数据类型
     * @param returnType 数据类型对应的 java 包装类型
     * @param <T> 数据类型对应的 java 包装类型
     * @return 传进来的类型
     */
    <T> T readHoldingRegister(int slaveId, int offset, int dataType, Class<T> returnType)
            throws ModbusTransportException, ErrorResponseException;

    /**
     * 读取[04 Input Registers 3x]类型。模拟量数据，04表示功能码，3x表示地址首位为3开头
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return 模拟量 = 整数、小数
     */
    Number readInputRegisters(int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException;

    /**
     * 自定义类型
     * 读取[04 Input Registers 3x]类型。模拟量数据，04表示功能码，3x表示地址首位为3开头
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return 模拟量 = 整数、小数
     */
    <T> T readInputRegisters(int slaveId, int offset, int dataType, Class<T> returnType)
            throws ModbusTransportException, ErrorResponseException;

    /*----------------------------------04功能码就不做了，都是一样的-----------------------------------------------*/
    /**
     * 读取03功能码的 short 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Short
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Short read03Short(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException;


    /**
     * 读取03功能码的无符号 short 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Short
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Short read03UnsignedShort(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException;

    /**
     * 读取03功能码的 Integer 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Integer
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Integer read03Int(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException;

    /**
     * 读取03功能码的反转 Integer 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Integer
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Integer read03IntInverse(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException;

    /**
     * 读取03功能码的无符号 Integer 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Integer
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Integer read03IntUnsigned(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException;

    /**
     * 读取03功能码的无符号+反转 Integer 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Integer
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Integer read03IntUnsignedInverse(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException;

    /**
     * 读取03功能码的 Long 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Long
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Long read03Long(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException;


    /**
     * 读取03功能码的反转 Long 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Long
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Long read03LongInverse(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException;


    /**
     * 读取03功能码的无符号 Long 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Long
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Long read03LongUnsigned(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException;


    /**
     * 读取03功能码的无符号+反转 Long 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Long
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Long read03LongUnsignedInverse(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException;


    /**
     * 读取03功能码的 Float 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Float
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Float read03FloatABCD(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException;

    /**
     * 读取03功能码的 Float 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Float
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Float read03Float(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException;


    /**
     * 读取03功能码的反转 Float 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Float
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Float read03FloatCDAB(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException;


    /**
     * 读取03功能码的反转 Float 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Float
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Float read03FloatInverse(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException;


    /**
     * 读取03功能码的 Double 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Double
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Double read03DoubleABCDEFGH(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException;


    /**
     * 读取03功能码的 Double 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Double
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Double read03Double(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException;


    /**
     * 读取03功能码的反转 Double 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Double
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Double read03DoubleGHEFCDAB(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException;


    /**
     * 读取03功能码的反转 Double 类型数据
     * @param slaveId 从设备id
     * @param offset 地址位
     * @return Double
     * @throws ModbusTransportException 传输异常
     * @throws ErrorResponseException 错误响应异常
     */
    Double read03DoubleInverse(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException;


    /**
     * 批量读取，推荐使用，不建议一个批量读取中存在不同的从设备，虽然可能modbus4j能够处理好，但是不符合modbus通讯标准
     * @param slaveId 从设备id
     * @param offsetList 地址位集合
     * @param dataType 数据类型，参考 DataType 类
     * @param returnType 返回的数据类型
     * @param <T> 返回的数据类型
     * @return T
     * @throws ErrorResponseException 错误响应异常
     * @throws ModbusTransportException 传输异常
     */
    <T> Map<Integer, T> batchRead(int slaveId, List<Integer> offsetList, int dataType, Class<T> returnType)
            throws ErrorResponseException, ModbusTransportException;


    /**
     * 批量读取，不推荐使用
     * @param batchReadParamList 参数集合
     * @param dataType 数据类型，参考 DataType 类
     * @param returnType 返回的数据类型
     * @param <T> 返回的数据类型
     * @return T
     * @throws ErrorResponseException 错误响应异常
     * @throws ModbusTransportException 传输异常
     */
    <T> Map<Integer, T> batchRead(List<BatchReadParam> batchReadParamList, int dataType, Class<T> returnType)
            throws ErrorResponseException, ModbusTransportException;

    /* -------------------------------------以下是写入----------------------------------------- */

    /**
     * 写 [01 Coil Status(0x)]，线圈，开关量，布尔值
     * @param slaveId 从设备id
     * @param writeOffset 写入的地址位
     * @param writeValue 写入的值
     * @return 写入结果
     * @throws ModbusTransportException modbus 传输异常
     */
    boolean writeCoil(int slaveId, int writeOffset, boolean writeValue)
            throws ModbusTransportException;


    /**
     * 批量写 [01 Coil Status(0x)]，线圈，开关量，布尔值
     * @param slaveId 从设备id
     * @param startOffset 写入的起始地址位，以起始地址位开始自动向下递增写
     * @param writeValueArr 写入的值数组
     * @return 写入结果
     * @throws ModbusTransportException modbus 传输异常
     */
    boolean batchWriteCoil(int slaveId, int startOffset, boolean[] writeValueArr)
            throws ModbusTransportException;


    /**
     * 写 [03 Holding Register(4x)]，小于等于2字节的整数
     * @param slaveId 从设备id
     * @param writeOffset 写入的地址位
     * @param writeValue 写入的值
     * @return 写入结果
     * @throws ModbusTransportException modbus 传输异常
     */
    boolean writeRegister(int slaveId, int writeOffset, short writeValue)
            throws ModbusTransportException;


    /**
     * 批量写 [03 Holding Register(4x)]，小于等于2字节的整数
     * @param slaveId 从设备id
     * @param startOffset 写入的起始地址位，以起始地址位开始自动向下递增写
     * @param writeValueArr 写入的值数组
     * @return 写入结果
     * @throws ModbusTransportException modbus 传输异常
     */
    boolean batchWriteRegister(int slaveId, int startOffset, short[] writeValueArr)
            throws ModbusTransportException;


    /**
     * 写 [03 Holding Register(4x)]，模拟量、整数、小数
     * @param slaveId 从设备id
     * @param offset 写入的地址位
     * @param value 写入的值
     * @param dataType 数据类型，参考 DataType 类，需要和实际写入的数值对应上
     * @throws ModbusTransportException modbus 传输异常
     * @throws ErrorResponseException modbus 响应异常
     */
    void writeHoldingRegister(int slaveId, int offset, Number value, int dataType)
            throws ModbusTransportException, ErrorResponseException;
}
